Google Cloud の不要リソースを Asset Inventory で断捨離してみた。

Google Cloud の不要リソースを Asset Inventory で断捨離してみた。

Clock Icon2024.08.26

こんにちは、みかみです。

今年は本州経由の台風が多い気がしますが、みなさまどうぞお気をつけください。

やりたいこと

  • Google Cloud の不要なリソースを整理したい
  • プロジェクト内のリソース一覧を確認したい
  • リソース情報を BigQuery にエクスポートしたい

前提

Google Cloud SDK(gcloud コマンド)の実行環境は準備済みであるものとします。 本エントリでは、Cloud Shell を使用しました。

また、Cloud Asset Inventory や BigQuery など各サービス操作に必要な API の有効化と必要な権限は付与済みです。

なお、文中、プロジェクトIDなど一部の文字は伏字に変更しています。

Cloud Asset Inventory とは

Google Cloud のリソースを管理するサービスで、利用料金はかかりません。
どのリージョンにどんなリソースがあるのか、どのリソースに誰がアクセスできるかなどを確認することができます。
管理コンソールからも、リソースの検索やリソースが存在するリージョンのマップ表示などを確認できます。

リソースの一覧を gcloud コマンドで出力することもできますが、大量のリソースがあると確認が大変です。
今回は、リソース情報を BigQuery にエクスポートして、SQL で確認してみます。

リソース情報を BigQuery に出力する Python コードを準備

Cloud Asset Inventory を使用して、リソース情報を BigQuery に出力する Python コードを準備します。

下記コードを参考にさせていただきました。

以下の Python コードを Cloud Shell から実行します。

from google.cloud import asset_v1, bigquery
import datetime

def export_assets_to_bigquery(project_id, dataset_id, table_prefix):
    # Initialisation
    bq_client = bigquery.Client()
    asset_client = asset_v1.AssetServiceClient()

    # BigQuery variables
    dataset_id = "check_resource"
    table_id = f'{table_prefix}_{datetime.datetime.utcnow().strftime("%d%m%Y_%H%M%S")}'
    bigquery_destination = f"projects/{project_id}/datasets/{dataset_id}"

    # Prepare the output configuration
    output_config = asset_v1.OutputConfig(
        bigquery_destination=asset_v1.BigQueryDestination(
            dataset=bigquery_destination,
            table=table_id,
            force=True
        )
    )

    # Prepare the request
    request = asset_v1.ExportAssetsRequest(
        parent=f"projects/{project_id}",
        content_type=asset_v1.ContentType.RESOURCE,
        output_config=output_config
    )

    try:
        operation = asset_client.export_assets(request=request)
        response = operation.result()
        print(f"Successfully exported assets to BigQuery table: {table_id}")
    except Exception as e:
        print(f"Failed to export assets: {e}")

project_id = "[PROJECT_ID]"
dataset_id = "check_resource"
table_prefix = "asset_inventory"

export_assets_to_bigquery(project_id, dataset_id, table_prefix)

BigQuery にリソース情報が格納されました。

asset_inventory_25082024_121942_1

asset_inventory_25082024_121942_mod

出力されたリソース情報を確認&断捨離実行

BigQuery で SQL を実行して、出力したリソースデータを確認します。

asset_inventory_25082024_121942_count

全部で1753個のリソースがあるようです。。

どのサービスのリソースが多いのか確認してみます。

asset_inventory_25082024_121942_assets

アセットタイプがどのサービスに相当するのか、以下のドキュメントで確認できます。

Container Registry のイメージが大部分を占めているようです。Artifact Registry の Docker イメージも溜まってます。。
サービスアカウントとアカウントキーもだいぶ多いようです。。
BigQuery テーブルも200ほどありますが、こちらは別途確認するとして、いったん残しておくことにします。

Container Registry のイメージを削除

Container Registry はもう非推奨です。
下記コマンドでイメージが格納されているバケットを削除しました。

gsutil rm -r gs://asia.artifacts.[PROJECT_ID].appspot.com

Artifact Registry にクリーンアップポリシーを設定

Artifact Registry の Docker イメージは、クリーンアップポリシーを適用して今後不要なイメージが溜まらないように設定しておきます。

以下の、最新から3代目までのイメージを保持してそれ以外は削除するポリシーを定義した、JSONファイルを準備しました。

[
    {
      "name": "keep-last-3",
      "action": {"type": "Keep"},
      "mostRecentVersions": {
        "keepCount": 3
      }
    },
    {
      "name": "delete",
      "action": {"type": "Delete"},
      "condition": {
        "tagState": "any"
      }
    }
]

まずは下記コマンドで dry_run してみます。

gcloud artifacts repositories set-cleanup-policies gcf-artifacts \
    --project=[PROJECT_ID] \
    --location=asia-northeast1 \
    --policy=cleanup_policy.json \
    --dry-run

dry_run モードで意図通りのクリーンアップポリシーの適用が確認できたので、--no-dry-run で実行します。

$  gcloud artifacts repositories set-cleanup-policies gcf-artifacts \
    --project=[PROJECT_ID] \
    --location=asia-northeast1 \
    --policy=cleanup_policy.json \
    --no-dry-run
Updated repository [gcf-artifacts].
Dry run is disabled.
[
  {
    "action": {
      "type": "DELETE"
    },
    "condition": {
      "tagState": "ANY"
    },
    "name": "delete"
  },
  {
    "action": {
      "type": "KEEP"
    },
    "mostRecentVersions": {
      "keepCount": 3
    },
    "name": "keep-last-3"
  }
]

クリーンアップポリシーが適用できました。

artifacts_set-cleanup-policies

不要なサービスアカウントを削除

以下のクエリで、1年以上更新のないサービスアカウントを確認します。

select
  name,
  replace(json_extract(resource.data, '$.description'), "\"", "") as description,
  replace(json_extract(resource.data, '$.displayName'), "\"", "") as displayName,
  replace(json_extract(resource.data, '$.email'), "\"", "") as email,
  update_time
from
  check_resource.asset_inventory_25082024_121942
where
  asset_type = 'iam.googleapis.com/ServiceAccount'
  and update_time < TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 360 DAY)
order by update_time

検証用に作成して残ったままになっていた不要なサービスアカウントを、以下のコマンドで削除しました。

gcloud iam service-accounts delete \
    SA_NAME@PROJECT_ID.iam.gserviceaccount.com

また、削除しないサービスアカウントのキーは、下記 SQL でキーIDを確認してから

select
  name,
  split(replace(json_extract(resource.data, '$.name'), "\"", ""), '/')[3] as sa_id,
  split(replace(json_extract(resource.data, '$.name'), "\"", ""), '/')[5] as key_id,
  update_time
from
  check_resource.asset_inventory_25082024_121942
where
  asset_type = 'iam.googleapis.com/ServiceAccountKey'
  and split(json_extract(resource.data, '$.name'), '/')[3] = 'temp-sa-rotation@cm-da-mikami-yuki-258308.iam.gserviceaccount.com'
order by update_time

下記コマンドで削除しました。

gcloud iam service-accounts keys delete KEY_ID \
    --iam-account=temp-sa-rotation@PROJECT_ID.iam.gserviceaccount.com

不要なリソースを断捨離できました。

まとめ(所感)

特に検証環境では、「使い終わったら削除する」ルールを決めておいても、削除忘れが発生するものではないかと思います。
今回はワンショットで実行しましたが、Python コードを Cloud Functions にデプロイして週次や月次でスケジュール実行し、BigQuery に出力されたデータをチェックして、新しく作成されたリソースや最終更新後一定期間以上経過したリソースなどを通知するバッチ処理を実装しておくと、便利ではないかと思います。

不要リソースは残っていても困らないケースも多いかと思いますが、長期間残ったままだと、いざ整理しようとした時に消して良いのか悪いのか、判断に困る事態に陥るかもしれません。
不要なリソースを削除することはコスト削減や保守性向上にもつながるので、日々いらないリソースを整理して、シンプルで心地よい環境を維持しようと思いました。

参考

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.